package com.ky.pro.java.rpc.core.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.ky.pro.java.rpc.core.annotation.StubService;
import com.ky.pro.java.rpc.core.api.RpcfxException;
import com.ky.pro.java.rpc.core.api.RpcfxRequest;
import com.ky.pro.java.rpc.core.api.RpcfxResponse;
import com.ky.pro.java.rpc.core.config.KyRpcProperties;
import com.ky.pro.java.rpc.core.config.StubProperties;
import com.ky.pro.java.rpc.core.util.http.NettyHttpClient;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.URL;

/**
 * @ClassName StubServiceAspect
 * @Description TODO
 * @Date 2022/1/1 15:13
 * @Author kevin
 **/
@Component
@Aspect
@Slf4j
public class StubServiceAspect implements DisposableBean {

    static {
        ParserConfig.getGlobalInstance().addAccept("com.ky.pro.java.rpc");
    }

    @Autowired
    KyRpcProperties kyRpcProperties;

    @Autowired
    NettyHttpClient nettyHttpClient;

    @Pointcut("@annotation(com.ky.pro.java.rpc.core.annotation.StubService) || @within(com.ky.pro.java.rpc.core.annotation.StubService)")
    public void stubServicePointCut() {

    }

    @Around("stubServicePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        StubService stubServiceAnnotation = point.getTarget().getClass().getDeclaredAnnotation(StubService.class);
        String serviceName = stubServiceAnnotation.name();
        Class<?> stubClz = stubServiceAnnotation.stubClass();
        StubProperties stubProperties = kyRpcProperties.getStubProperties(serviceName);

        if (stubProperties == null) {
            throw new RuntimeException(serviceName + "未配置！");
        }

        String serviceUrl = stubProperties.getServerUrl();
        if (serviceUrl == null) {
            throw new RuntimeException(serviceName + "地址未配置！");
        }

        RpcfxRequest request = new RpcfxRequest();
        request.setServiceClass(stubClz.getName());
        request.setMethod(method.getName());
        request.setParams(point.getArgs());

        RpcfxResponse response = post(request, serviceUrl);

        // 这里判断response.status，处理异常
        if (!response.isStatus()) {
            Exception e = response.getException();
            if (e instanceof RpcfxException) {
                // 模拟处理RPC异常
                log.error("发生RPC异常", e);
            }
        }

        return JSON.parse(response.getResult().toString());
    }

    private RpcfxResponse post(RpcfxRequest req, String urlStr) throws IOException {
        String reqJson = JSON.toJSONString(req);
        log.info("req json: " + reqJson);

        // 1.可以复用client
        // 2.尝试使用httpclient或者netty client

        nettyHttpClient = new NettyHttpClient();

        URL url = new URL(urlStr);
        HttpHeaders httpHeaders = new DefaultHttpHeaders();
        httpHeaders.add("Content-Type", "application/json; charset=utf-8");
        httpHeaders.add("Content-Length", reqJson.length());
        httpHeaders.add("Host", url.getHost() + ":" + url.getPort());

        DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
                "/", Unpooled.copiedBuffer(reqJson, CharsetUtil.UTF_8), httpHeaders, httpHeaders);

        FullHttpResponse response = (FullHttpResponse) nettyHttpClient.sendRequest(request,
                new InetSocketAddress(url.getHost(),url.getPort()));

        String respJson = response.content().toString(CharsetUtil.UTF_8);

        return JSON.parseObject(respJson, RpcfxResponse.class);
    }

    @Override
    public void destroy() throws Exception {
        nettyHttpClient.shutdown();
    }
}
